{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "1c2261ce-41d8-45c2-90a2-48dfedfbbf3f",
   "metadata": {},
   "source": [
    "# 第3章：NumPy运算\n",
    "\n",
    "NumPy提供了强大的运算功能，使得对数组的数学和统计操作变得高效而简单。本章将详细介绍NumPy的运算能力，帮助你掌握如何使用它进行数值计算。\n",
    "\n",
    "## 3.1 基本数学运算\n",
    "\n",
    "NumPy支持对数组进行**逐元素运算**，这是一种向量化操作，可以避免使用循环，提高计算效率。我们将介绍逐元素运算、标量与数组的运算，以及通用函数（ufunc）。\n",
    "\n",
    "### 3.1.1 逐元素运算\n",
    "\n",
    "NumPy中的基本运算符（如 `+`、`-`、`*`、`/`）会对数组的每个元素执行相同操作。\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "aae45312-fe64-4e38-88a8-3cbae888992b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a + b: [ 6  8 10 12]\n",
      "a - b: [-4 -4 -4 -4]\n",
      "a * b: [ 5 12 21 32]\n",
      "a / b: [0.2        0.33333333 0.42857143 0.5       ]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "\n",
    "# 创建两个一维数组\n",
    "a = np.array([1, 2, 3, 4])\n",
    "b = np.array([5, 6, 7, 8])\n",
    "\n",
    "# 逐元素加法\n",
    "print(\"a + b:\", a + b)  # 输出: [ 6  8 10 12]\n",
    "\n",
    "# 逐元素减法\n",
    "print(\"a - b:\", a - b)  # 输出: [-4 -4 -4 -4]\n",
    "\n",
    "# 逐元素乘法\n",
    "print(\"a * b:\", a * b)  # 输出: [ 5 12 21 32]\n",
    "\n",
    "# 逐元素除法\n",
    "print(\"a / b:\", a / b)  # 输出: [0.2        0.33333333 0.42857143 0.5       ]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db8bdc78-852b-4653-8a5a-b659009b89c6",
   "metadata": {},
   "source": [
    "**讲解：**\n",
    "\n",
    "- 逐元素运算要求两个数组的形状相同（或者符合广播规则，详见3.3节）。\n",
    "- 这种操作比Python循环更快，因为NumPy在底层使用了优化的C代码。\n",
    "\n",
    "### 3.1.2 标量与数组的运算\n",
    "\n",
    "标量（单个数字）可以与数组运算，标量会自动扩展到与数组相同的形状。\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "97bcc25a-bf0a-40d8-bb46-1ffdb98c43f3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a + 10: [11 12 13 14]\n",
      "a * 2: [2 4 6 8]\n",
      "a / 2: [0.5 1.  1.5 2. ]\n"
     ]
    }
   ],
   "source": [
    "# 创建一个数组\n",
    "a = np.array([1, 2, 3, 4])\n",
    "\n",
    "# 标量加法\n",
    "print(\"a + 10:\", a + 10)  # 输出: [11 12 13 14]\n",
    "\n",
    "# 标量乘法\n",
    "print(\"a * 2:\", a * 2)    # 输出: [2 4 6 8]\n",
    "\n",
    "# 标量除法\n",
    "print(\"a / 2:\", a / 2)    # 输出: [0.5 1.  1.5 2. ]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f1be917f-1cf3-4020-bdd3-c6b085724ed2",
   "metadata": {},
   "source": [
    "**讲解：**\n",
    "\n",
    "- 标量运算利用了广播机制（详见3.3节），无需手动扩展标量。\n",
    "- 这使得代码更简洁，计算更高效。\n",
    "\n",
    "### 3.1.3 通用函数（ufunc）\n",
    "\n",
    "NumPy提供了**通用函数（ufunc）**，这些函数对数组的每个元素应用数学操作，例如三角函数、指数、对数等。\n",
    "\n",
    "**常用ufunc：**\n",
    "\n",
    "- `np.sin()`：正弦函数\n",
    "- `np.cos()`：余弦函数\n",
    "- `np.exp()`：指数函数\n",
    "- `np.log()`：自然对数\n",
    "- `np.sqrt()`：平方根\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "044ab2e9-9c95-4068-bf9d-b6a6a66f6cc3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sin(a): [0.0000000e+00 1.0000000e+00 1.2246468e-16]\n",
      "exp(a): [ 1.          4.81047738 23.14069263]\n",
      "sqrt(a): [0.         1.25331414 1.77245385]\n"
     ]
    }
   ],
   "source": [
    "# 创建一个数组\n",
    "a = np.array([0, np.pi/2, np.pi])\n",
    "\n",
    "# 计算正弦\n",
    "print(\"sin(a):\", np.sin(a))  # 输出: [0.0000000e+00 1.0000000e+00 1.2246468e-16]\n",
    "\n",
    "# 计算指数\n",
    "print(\"exp(a):\", np.exp(a))  # 输出: [ 1.          4.81047738 23.14069263]\n",
    "\n",
    "# 计算平方根\n",
    "print(\"sqrt(a):\", np.sqrt(a))  # 输出: [0.         1.25331414 1.77245385]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b0e7fd7f-87a2-40a0-955b-aaa9ee7eadce",
   "metadata": {},
   "source": [
    "**讲解：**\n",
    "\n",
    "- ufunc是NumPy的核心功能，专门为数组运算优化。\n",
    "- 这些函数比Python内置数学函数（如 `math.sin`）更快，适合处理大量数据。\n",
    "\n",
    "---\n",
    "\n",
    "## 3.2 统计函数\n",
    "\n",
    "NumPy提供了丰富的统计函数，用于计算数组的统计量，如求和、平均值、标准差等。可以对整个数组或沿指定轴计算。\n",
    "\n",
    "### 3.2.1 基本统计函数\n",
    "\n",
    "**常用统计函数：**\n",
    "\n",
    "- `np.sum()`：求和\n",
    "- `np.mean()`：平均值\n",
    "- `np.std()`：标准差\n",
    "- `np.var()`：方差\n",
    "- `np.min()`：最小值\n",
    "- `np.max()`：最大值\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6b085725-ed2f-4187-bbaf-01a37b37fbdb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "所有元素之和: 45\n",
      "每列之和: [12 15 18]\n",
      "每行之和: [ 6 15 24]\n",
      "所有元素平均值: 5.0\n",
      "标准差: 2.581988897471611\n"
     ]
    }
   ],
   "source": [
    "# 创建一个二维数组\n",
    "arr = np.array([[1, 2, 3],\n",
    "                [4, 5, 6],\n",
    "                [7, 8, 9]])\n",
    "\n",
    "# 对整个数组求和\n",
    "print(\"所有元素之和:\", np.sum(arr))  # 输出: 45\n",
    "\n",
    "# 按列求和（沿axis=0）\n",
    "print(\"每列之和:\", np.sum(arr, axis=0))  # 输出: [12 15 18]\n",
    "\n",
    "# 按行求和（沿axis=1）\n",
    "print(\"每行之和:\", np.sum(arr, axis=1))  # 输出: [ 6 15 24]\n",
    "\n",
    "# 计算平均值\n",
    "print(\"所有元素平均值:\", np.mean(arr))  # 输出: 5.0\n",
    "\n",
    "# 计算标准差\n",
    "print(\"标准差:\", np.std(arr))  # 输出: 2.581988897471611"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a3d0578-1421-4935-9f6c-e7887ea90d18",
   "metadata": {},
   "source": [
    "**讲解：**\n",
    "\n",
    "- 参数 `axis` 指定运算方向：`0` 表示按列，`1` 表示按行。\n",
    "- 这些函数返回标量（对整个数组）或数组（沿轴计算）。\n",
    "\n",
    "### 3.2.2 最大值和最小值及其索引\n",
    "\n",
    "**相关函数：**\n",
    "\n",
    "- `np.min()`：最小值\n",
    "- `np.max()`：最大值\n",
    "- `np.argmin()`：最小值索引\n",
    "- `np.argmax()`：最大值索引\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f02b6894-a544-4264-9515-df9df97e62c4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "最小值: 10\n",
      "最大值: 30\n",
      "最小值索引: 0\n",
      "最大值索引: 2\n"
     ]
    }
   ],
   "source": [
    "# 创建一个一维数组\n",
    "arr = np.array([10, 20, 30, 25, 15])\n",
    "\n",
    "# 最小值和最大值\n",
    "print(\"最小值:\", np.min(arr))  # 输出: 10\n",
    "print(\"最大值:\", np.max(arr))  # 输出: 30\n",
    "\n",
    "# 最小值和最大值的索引\n",
    "print(\"最小值索引:\", np.argmin(arr))  # 输出: 0\n",
    "print(\"最大值索引:\", np.argmax(arr))  # 输出: 2"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c374ef0f-4294-4643-8ec6-abcfbb1784a0",
   "metadata": {},
   "source": [
    "**讲解：**\n",
    "\n",
    "- `argmin` 和 `argmax` 返回展平后的索引。\n",
    "- 对于多维数组，可用 `np.unravel_index()` 转换为多维坐标。\n",
    "\n",
    "---\n",
    "\n",
    "## 3.3 广播机制\n",
    "\n",
    "**广播（Broadcasting）** 是NumPy中一个强大功能，允许不同形状的数组进行运算。它通过自动扩展数组的维度来实现。\n",
    "\n",
    "### 3.3.1 广播的规则\n",
    "\n",
    "1. 如果两个数组维度不同，低维数组会在前面补1，直到维度相同。\n",
    "2. 每个维度上，形状相同或其中一个为1，则兼容。\n",
    "3. 否则，无法广播。\n",
    "\n",
    "**示例：**\n",
    "\n",
    "- `(3, 2)` 和 `(2,)` 可以广播（第二个维度兼容）。\n",
    "- `(3, 2)` 和 `(3, 1)` 可以广播（第二个维度为1）。\n",
    "- `(3, 2)` 和 `(2, 3)` 无法广播。\n",
    "\n",
    "### 3.3.2 广播的应用\n",
    "\n",
    "**示例1：标量与数组广播**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "b7b17709-6887-4166-abf6-c39ed4444b12",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[11 12 13]\n",
      " [14 15 16]]\n"
     ]
    }
   ],
   "source": [
    "# 创建二维数组\n",
    "arr = np.array([[1, 2, 3],\n",
    "                [4, 5, 6]])\n",
    "\n",
    "# 标量广播\n",
    "result = arr + 10\n",
    "print(result)\n",
    "# 输出:\n",
    "# [[11 12 13]\n",
    "#  [14 15 16]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6d461307-ca27-4d39-823f-a554552c11ca",
   "metadata": {},
   "source": [
    "**示例2：一维与二维数组广播**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7cd691b1-65c8-453e-8ac2-dd9d3623e84d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[11 22 33]\n",
      " [14 25 36]]\n"
     ]
    }
   ],
   "source": [
    "# 创建二维和一维数组\n",
    "arr2d = np.array([[1, 2, 3],\n",
    "                  [4, 5, 6]])\n",
    "arr1d = np.array([10, 20, 30])\n",
    "\n",
    "# 一维数组广播到每行\n",
    "result = arr2d + arr1d\n",
    "print(result)\n",
    "# 输出:\n",
    "# [[11 22 33]\n",
    "#  [14 25 36]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c13d6588-1683-4f49-89ef-d15dbf2507a1",
   "metadata": {},
   "source": [
    "**示例3：列向量与行向量广播**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "67af69b3-2e54-49e3-87b9-b2a67248a4ae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[1]\n",
      " [2]\n",
      " [3]]\n",
      "[10 20 30]\n",
      "[[11 21 31]\n",
      " [12 22 32]\n",
      " [13 23 33]]\n"
     ]
    }
   ],
   "source": [
    "# 创建列向量和行向量\n",
    "col_vec = np.array([[1], [2], [3]])  # 形状 (3, 1)\n",
    "row_vec = np.array([10, 20, 30])     # 形状 (3,)\n",
    "print(col_vec)\n",
    "print(row_vec)\n",
    "# 广播生成矩阵\n",
    "result = col_vec + row_vec\n",
    "print(result)\n",
    "# 输出:\n",
    "# [[11 21 31]\n",
    "#  [12 22 32]\n",
    "#  [13 23 33]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "547d50f2-df95-4901-8b88-91f6c6714ff5",
   "metadata": {},
   "source": [
    "**讲解：**\n",
    "\n",
    "- 广播简化了代码，避免了手动扩展数组。\n",
    "- 使用前需检查形状兼容性，以防错误。\n",
    "\n",
    "---\n",
    "\n",
    "## 3.4 线性代数操作\n",
    "\n",
    "NumPy支持线性代数运算，适用于矩阵计算。以下为常用操作：\n",
    "\n",
    "### 3.4.1 矩阵乘法\n",
    "\n",
    "**函数：**\n",
    "\n",
    "- `np.dot()`：点积\n",
    "- `np.matmul()`：矩阵乘法（推荐）\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "55127fff-baa9-420d-aa9a-4642bd1fa9dc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[19 22]\n",
      " [43 50]]\n",
      "[[19 22]\n",
      " [43 50]]\n"
     ]
    }
   ],
   "source": [
    "# 创建两个矩阵\n",
    "a = np.array([[1, 2],\n",
    "               [3, 4]])\n",
    "b = np.array([[5, 6],\n",
    "               [7, 8]])\n",
    "\n",
    "result1=np.dot(a,b)\n",
    "print(result1)\n",
    "result = np.matmul(a, b)\n",
    "print(result)\n",
    "# 输出:\n",
    "# [[19 22]\n",
    "#  [43 50]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a137574d-46e6-4678-9f48-4db016cb7704",
   "metadata": {},
   "source": [
    "### 3.4.2 逆矩阵\n",
    "\n",
    "**函数：**\n",
    "\n",
    "- `np.linalg.inv()`：计算逆矩阵\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "f1645551-7937-4276-b90f-129004a3e38c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[-2.   1. ]\n",
      " [ 1.5 -0.5]]\n"
     ]
    }
   ],
   "source": [
    "# 创建可逆矩阵\n",
    "a = np.array([[1, 2],\n",
    "              [3, 4]])\n",
    "\n",
    "# 计算逆矩阵\n",
    "inv_a = np.linalg.inv(a)\n",
    "print(inv_a)\n",
    "# 输出:\n",
    "# [[-2.   1. ]\n",
    "#  [ 1.5 -0.5]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "327ef8be-ff6c-493d-934a-81a5d701c8f7",
   "metadata": {},
   "source": [
    "对于非方阵或不可逆矩阵，可以使用伪逆（Moore-Penrose逆），通过`np.linalg.pinv()`计算。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "575eef0d-cd92-4d64-826a-5235a8a2d891",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "伪逆:\n",
      " [[-0.94444444  0.44444444]\n",
      " [-0.11111111  0.11111111]\n",
      " [ 0.72222222 -0.22222222]]\n"
     ]
    }
   ],
   "source": [
    "# 创建一个非方阵\n",
    "b = np.array([[1, 2, 3], [4, 5, 6]])\n",
    "\n",
    "# 求伪逆\n",
    "pinv_b = np.linalg.pinv(b)\n",
    "print(\"伪逆:\\n\", pinv_b)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "183d736f-fb7e-4b9a-81b5-d940be0db99e",
   "metadata": {},
   "source": [
    "### 3.4.3 特征值和特征向量\n",
    "\n",
    "**函数：**\n",
    "\n",
    "- `np.linalg.eig()`：计算特征值和特征向量\n",
    "\n",
    "**示例：**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "ef27633b-418c-4513-94ea-b531bf6a04f4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "特征值: [ 3. -1.]\n",
      "特征向量:\n",
      " [[ 0.70710678 -0.70710678]\n",
      " [ 0.70710678  0.70710678]]\n"
     ]
    }
   ],
   "source": [
    "# 创建矩阵\n",
    "a = np.array([[1, 2],\n",
    "              [2, 1]])\n",
    "\n",
    "# 计算特征值和特征向量\n",
    "eigvals, eigvecs = np.linalg.eig(a)\n",
    "print(\"特征值:\", eigvals)\n",
    "print(\"特征向量:\\n\", eigvecs)\n",
    "# 输出:\n",
    "# 特征值: [ 3. -1.]\n",
    "# 特征向量:\n",
    "# [[ 0.70710678 -0.70710678]\n",
    "#  [ 0.70710678  0.70710678]]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc4a3923-b145-4900-8eff-a8457d7d45b4",
   "metadata": {},
   "source": [
    "### 奇异值分解（SVD）\n",
    "\n",
    "奇异值分解（SVD）是一种通用的矩阵分解方法，适用于任意矩阵。使用`np.linalg.svd()`计算。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "18394dcf-111f-40ba-9b15-cbeda86b1253",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "U:\n",
      " [[-0.2298477   0.88346102  0.40824829]\n",
      " [-0.52474482  0.24078249 -0.81649658]\n",
      " [-0.81964194 -0.40189603  0.40824829]]\n",
      "S: [9.52551809 0.51430058]\n",
      "V:\n",
      " [[-0.61962948 -0.78489445]\n",
      " [-0.78489445  0.61962948]]\n"
     ]
    }
   ],
   "source": [
    "# 创建一个矩阵\n",
    "a = np.array([[1, 2], [3, 4], [5, 6]])\n",
    "\n",
    "# 进行SVD分解\n",
    "U, S, V = np.linalg.svd(a)\n",
    "print(\"U:\\n\", U)\n",
    "print(\"S:\", S)\n",
    "print(\"V:\\n\", V)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ed882e3c-3ab6-459d-a69b-05dc82a98137",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:base] *",
   "language": "python",
   "name": "conda-base-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
